package com.linzm.context;

import cn.hutool.core.lang.ClassScanner;
import com.linzm.annotation.Autowired;
import com.linzm.annotation.Component;
import lombok.SneakyThrows;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Date: 2023/11/9 0:02
 * @User linziming
 */
public class IocApplicationContext {
    private static final Map<Class<?>, Object> classBeanMap = new ConcurrentHashMap<>();

    private static final Map<String, Object> clzNameBeanMap = new ConcurrentHashMap<>();

    static {
        newInstance();
        doInjection();
        setClzNameBeanMap();
    }

    private static void setClzNameBeanMap() {
        for (Map.Entry<Class<?>, Object> entry : classBeanMap.entrySet()) {
            clzNameBeanMap.putIfAbsent(entry.getKey().getTypeName(), entry.getValue());
        }
    }

    /**
     * 构建 bean 容器
     */
    @SneakyThrows
    public static void newInstance() {
        // 扫描包
        InputStream inputStream = IocApplicationContext.class.getClassLoader().getResourceAsStream("ioc.properties");
        Properties properties = new Properties();
        properties.load(inputStream);
        Set<Class<?>> classes = ClassScanner.scanPackage(properties.getProperty("scan.package"));
        for (Class<?> aClass : classes) {
            if (!aClass.isAnnotationPresent(Component.class)) {
                continue;
            }
            classBeanMap.putIfAbsent(aClass, aClass.newInstance());
            Class<?>[] interfaces = aClass.getInterfaces();
            for (Class<?> parentClass : interfaces) {
                classBeanMap.putIfAbsent(parentClass, aClass.newInstance());
            }
        }
    }

    /**
     * 依赖注入
     */
    @SneakyThrows
    private static void doInjection() {
        for (Map.Entry<Class<?>, Object> entry : classBeanMap.entrySet()) {
            Field[] declaredFields = entry.getKey().getDeclaredFields();
            for (Field field : declaredFields) {
                if (!field.isAnnotationPresent(Autowired.class)) {
                    continue;
                }
                Object fieldObj = classBeanMap.get(field.getType());
                if (fieldObj == null) {
                    fieldObj = field.getType().newInstance();
                }
                field.setAccessible(true);
                field.set(entry.getValue(), fieldObj);
            }
        }
    }

    public static Map<Class<?>, Object> getClassBeanMap() {
        return classBeanMap;
    }

    public static Object getObject(Class clz) {
        return classBeanMap.get(clz);
    }

    public static <T> T getBean(Class<T> targetClass) {
        return (T) classBeanMap.get(targetClass);
    }

    public static Map<String, Object> getClzNameBeanMap() {
        return clzNameBeanMap;
    }

    public static Object getObject(String completeClassName) {
        return clzNameBeanMap.get(completeClassName);
    }

    public static void updateBean(String completeClassName, Object object) {
        clzNameBeanMap.put(completeClassName, object);
    }

    public static <T> List<T> getBeans(Class<T> targetClass) {
        List<T> result = new ArrayList<>();
        for (Class<?> aClass : classBeanMap.keySet()) {
            if (targetClass.isAssignableFrom(aClass) && !targetClass.equals(aClass)) {
                result.add((T) classBeanMap.get(aClass));
            }
        }
        return result;
    }
}
